﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using QuickGraph;
using QuickGraph.Algorithms.ShortestPath;
using Harkon.AppServer.cOne.Registry;
using log4net;
using Harkon.AppServer.cOne.Service;
using Spring.Context;
using Spring.Context.Support;

namespace Harkon.AppServer.cOne.Deployment
{
    public class DeploymentManager : AbstractService, IDeploymentManager
    {
        public IServiceLifecycleManager LifecycleManager { get; set; }
        private static ILog logger = LogManager.GetLogger(typeof(DeploymentManager));

        #region IService

        public override bool Start()
        {
            if (base.Start())
            {
                LoadComponentDescriptions();
                CreateGraph();
                CheckCyclicDependency();

                //now deploy all found components
                DeployNewComponents();
                logger.Debug("DeploymentManager running");
                return true;
            }
            return false;
        }

        #endregion

        #region IDeploymentManager Members


        #endregion

        #region Deploying new found components
        private void DeployNewComponents()
        {
            foreach (var component in graph.Vertices)
            {
                //HostManager.CreateNewRemoteHost(component.Name);
                string xmlConfigFileName = string.Format("{0}-spring.config", component.Name);
                IApplicationContext context = new XmlApplicationContext(xmlConfigFileName);

                dynamic registryProxy = context["ServiceRegistryProxy"];
                registryProxy.ServiceRegistry = ServiceRegistry;

                ServiceRegistry.RegisterComponent(new ComponentInformation(component.Name, component.Autostart, component.Dependencies));

                if (component.Autostart)
                {
                    logger.InfoFormat("Component {0}: autostart enabled", component);
                    dynamic lifeCycleManager = context["ServiceLifecycleManager"];
                    lifeCycleManager.Init();
                    lifeCycleManager.Start();
                }else
                    logger.InfoFormat("Component {0}: autostart disabled", component);
            }
        }
        #endregion

        #region Description files loading and deserialization

        private ComponentList components = null;

        private void LoadComponentDescriptions()
        {
            FileInfo[] descriptionFiles = new DirectoryInfo(COneService.DeploymentPath).GetFiles("*description.xml");
            logger.Info(string.Format("Found {0} file{1} to deploy", descriptionFiles.Length, descriptionFiles.Length == 1 ? "" : "s"));
            components = new ComponentList();

            XmlSerializer serializer = new XmlSerializer(typeof(ComponentDescription));
            foreach (var descriptionFile in descriptionFiles)
            {
                TextReader reader = new StreamReader(descriptionFile.FullName);
                ComponentDescription description = (ComponentDescription)serializer.Deserialize(reader);
                components.FoundComponents.Add(description.Name, description);
            }
        }

        #endregion

        #region Graph analysis

        private ArrayAdjacencyGraph<ComponentDescription, SEdge<ComponentDescription>> graph;

        private void CreateGraph()
        {
            var edges = new List<SEdge<ComponentDescription>>();

            foreach (var description in components.FoundComponents)
            {
                if (description.Value.Dependencies != null)
                    foreach (var dependency in description.Value.Dependencies)
                    {
                        ComponentDescription source = components.FoundComponents[dependency];
                        SEdge<ComponentDescription> edge = new SEdge<ComponentDescription>(source, description.Value);
                        edges.Add(edge);
                        logger.Debug(string.Format("{0} depends on {1}", description.Value, source));
                    }
            }
            var tmpGraph = edges.ToAdjacencyGraph<ComponentDescription, SEdge<ComponentDescription>>();

            foreach (var description in components.FoundComponents)
            {
                if (!tmpGraph.ContainsVertex(description.Value))
                {
                    //independant vertex => add to graph
                    tmpGraph.AddVertex(description.Value);
                }
            }

            graph = tmpGraph.ToArrayAdjacencyGraph<ComponentDescription, SEdge<ComponentDescription>>();
        }

        private void CheckCyclicDependency()
        {
            var fw = new FloydWarshallAllShortestPathAlgorithm<ComponentDescription, SEdge<ComponentDescription>>(graph, GiveLength);
            fw.Compute();

            bool cyclicDependency = false;
            foreach (var edge in graph.Edges)
            {
                var source = edge.Source;
                var target = edge.Target;
                IEnumerable<SEdge<ComponentDescription>> path;
                cyclicDependency = fw.TryGetPath(target, source, out path);
                if (cyclicDependency)
                {
                    throw new CyclicDependencyException(source.Name);
                }
            }
        }

        private static double GiveLength(SEdge<ComponentDescription> edge)
        {
            return 1.0;
        }

        #endregion

        #region Temporary main for serialization

        public static void Main2(string[] args)
        {
            ComponentDescription descA = new ComponentDescription { Name = "ComponentA", Dependencies = new string[] { "ComponentC" } };
            ComponentDescription descB = new ComponentDescription { Name = "ComponentB", Dependencies = new string[] { "ComponentA", "ComponentC" } };
            ComponentDescription descC = new ComponentDescription { Name = "ComponentC", Dependencies = null };
            //ComponentDescription descC = new ComponentDescription { Name = "ComponentC", Dependencies = new string[] { "ComponentD" } };
            ComponentDescription descD = new ComponentDescription { Name = "ComponentD", Dependencies = new string[] { "ComponentA" } };

            XmlSerializer serializer = new XmlSerializer(typeof(ComponentDescription));
            TextWriter writerA = new StreamWriter(@"..\..\..\ServiceAContract\componenta-description.xml");
            TextWriter writerB = new StreamWriter(@"..\..\..\ServiceBContract\componentb-description.xml");
            TextWriter writerC = new StreamWriter(@"..\..\..\ServiceCContract\componentc-description.xml");
            TextWriter writerD = new StreamWriter(@"..\..\..\ServiceDContract\componentd-description.xml");
            serializer.Serialize(writerA, descA);
            writerA.Close();
            serializer.Serialize(writerB, descB);
            writerB.Close();
            serializer.Serialize(writerC, descC);
            writerC.Close();
            serializer.Serialize(writerD, descD);
            writerD.Close();
        }

        #endregion

    }
}
